#ifndef SCOPE_H
#define SCOPE_H

#include "Token.h"

struct FunctionName : public Symbol
{
	map<int,bool> args;
	bool reserved;
	FunctionName(int args, bool r = false)
	{
		this->args[args] = true;
		reserved = r;
	}

	string compile(Token* super)
	{
		if (!super->parent || (super->parent->id() != "(" && super->parent->id() != "new"))
		{
			super->error(Error::IsAFunc);
		}
		else 
		{
			Token* params = super->parent->arity == Token::Binary ? super->parent->second : super->parent->third;
			if (!args[params->leaves->size()])
				super->error(Error::DiffNbParam);
		}


		return super->value;
	}
};

struct VarName : public Symbol
{
	string compile(Token* super)
	{
		return super->value;
	}

	Type type;
	string refType;
};

struct ClassName : public Symbol
{
	string compile(Token* super)
	{
		return super->value;
	}

};


class Scope
{	
public:
	Scope()
	{
		parent = NULL;
	}

	void defineFunction (string n, int args) 
	{
		FunctionName* t = (FunctionName*)def[n];
		if (t) 
		{
			if (t->args[args])
				t->error(Error::AlreadyDefined);
			else
				t->args[args] = true;
		}
		else
		{
			def[n] = new FunctionName(args, true);
		}
	}

	Token* defineFunction (Token* n, int args, bool virt = false) 
	{
		FunctionName* t = (FunctionName*)def[n->value];
		if (t) 
		{
			if (t->args[args])
			{
				if (!virt)
					n->error(Error::AlreadyDefined);
			}
			else
				t->args[args] = true;
		}
		else
		{
			def[n->value] = new FunctionName(args);
		}
		return n;
	}

	void defineStaticFunction (string n, int args) 
	{
		FunctionName* t = (FunctionName*)static_def[n];
		if (t) 
		{
			if (t->args[args])
				t->error(Error::AlreadyDefined);
			else
				t->args[args] = true;
		}
		else
		{
			static_def[n] = new FunctionName(args, true);
		}
	}

	Token* defineStaticFunction (Token* n, int args) 
	{
		FunctionName* t = (FunctionName*)static_def[n->value];
		if (t) 
		{
			if (t->args[args])
				n->error(Error::AlreadyDefined);
			else
				t->args[args] = true;
		}
		else
		{
			static_def[n->value] = new FunctionName(args);
		}
		return n;
	}

	Token* defineVar(Token* n)
	{
		VarName* t = (VarName*)def[n->value];
		if (t) {
			n->error(Error::AlreadyDefined);
		}
		def[n->value] = new VarName();
		return n;
	}

	Symbol* find (const char* n) 
	{
		Scope* e = this; 
		Symbol* o;
		while (true) {
			o = e->def[n];
			if (o) {
				return o;
			}
			e = e->parent;
			if (!e) {
				return NULL;
			}
		}
	}

	void setType(const char* n, Symbol::Type type)
	{
		VarName* t = (VarName*)find(n);
		t->type = type;
	}

	void setType(const char* n, Token* tok)
	{
		VarName* t = (VarName*)find(n);
		t->type = tok->type();

		if (t->type == Symbol::Ref)
		{
			if (tok->id() == "new")
			{
				t->refType = tok->first->value;
			}
			else
			{
				t->refType = tok->scope->getRefType(tok->value);
			}
		}
	}

	Symbol::Type getType(const char* n)
	{
		VarName* t = (VarName*)find(n);
		return t->type;
	}

	string getRefType(const char* n)
	{
		VarName* t = (VarName*)find(n);
		return t->refType;
	}

	Symbol* findStatic (const char* n) 
	{
		return static_def[n];
	}

	void pop () 
	{
		scope = this->parent;
	}

	void reserve(Token* n) 
	{
		if (n->arity != Token::Name) 
		{
			return;
		}

		rev[n->value] = true;
	}

	bool reserved(Token *n)
	{
		if (n->arity != Token::Name)
			return true;

		Scope* e = this; 
		bool o = false;
		while (true) {
			o = e->rev[n->value];
			if (o) 
			{
				return o;
			}
			e = e->parent;
			if (!e) 
			{
				return false;
			}
		}

	}

	Scope* parent;
	map<string,Symbol*> def;
	map<string,Symbol*> static_def;
	map<string,bool> rev;
};

#endif
